package comunicaciones
import java.util.Date
import clientes.Cliente
import planes.PlanBasico
import planes.Plan

object Empresa {
  
  var clientes = Map[Cliente, List[Plan]]()
  
  def agregarCliente(cliente: Cliente) = clientes += (cliente -> List[Plan](new PlanBasico()))

  def valorComunicacion(comunicacion: Comunicacion): Double =

    comunicacion match {

      case LlamadaLocal(fecha, nroDestino, duracion) => 1 * duracion
      case LlamadaLargaDistancia(fecha, nroDestino, duracion, localidadOrigen, localidadDestino) => TablaDeLlamadas.valorLlamada(localidadOrigen, localidadDestino, duracion) * duracion
      case MensajeDeTexto(fecha, nroDestino, longitud) => (longitud / 140) * 0.10

    }

  def cantMinutosDeUnMes(mes: Int, cliente: Cliente): Int = {
    //Creo una lista vacia del tipo Llamada, ya que sino me jode con los tipos
    var llamadasRealizadasEnEseMes = List[Llamada]()
    //Filtro las comunicaciones que son de clase Llamada, y las agrego a la lista anteriormente creada
    cliente.comunicaciones.filter(c => c match {
      case Llamada(fecha, nroDestino, duracion) => c.fechaComunicacion().getMonth().equals(mes)
      case other => false
    }).map(a => llamadasRealizadasEnEseMes ::= a.asInstanceOf[Llamada])

    return llamadasRealizadasEnEseMes.foldLeft(0)((a, b) => a + b.duracion)

  }
  

  def comunicacionesLargas(mes: Int, cliente: Cliente): List[Comunicacion] = {

    //Retorno el filtro de aplicar a cada comunicacion su respectivo parametro de "largo"
    return cliente.comunicaciones.filter(com => com match {

      case MensajeDeTexto(fecha, nroDestino, longitud) => longitud > 140
      case Llamada(fecha, nroDestino, duracion) => duracion > 5
    })

  }
  
  def ciudadesDeComunicacionesLargas(mes: Int, cliente: Cliente): List[String] = {
    
		  //Filtro las LlamadasLargaDistancia
    return comunicacionesLargas(mes, cliente).filter(com => com match {
      
      case LlamadaLargaDistancia(fecha, nroDestino, origen, destino, duracion) => true
      case other => false
      
      	//Colecto sus localidades destino
    }).collect{ case a : LlamadaLargaDistancia => a.localidadDestino }
    
    
  }
  
  def diaQueMasComunicacionesRealizo(mes: Int, cliente: Cliente): Int = {
    //Filtro las comunicaciones del mes en cuestion, y a estas les pido el dia
    var listaDeDias = cliente.comunicaciones.filter(a => a.fechaComunicacion().getMonth().equals(mes)).collect{case a : Comunicacion => a.fechaComunicacion().getDate()}
    var listaDeParesDiaCant = crearPares(listaDeDias)
    var result = listaDeParesDiaCant.foldLeft((0,0))((a,b) => if(a._2 <= b._2) {b} else a)
    return result._1
    
  }
  
  def crearPares(listaDeDias: List[Int]): List[(Int, Int)] = {
    var xs = Map[Int, Int]()
    //Recorro la lista de dias, preguntando si en el MAP de pares dia/cantidad existe ese dia,
    //En caso de que exista, aumento el contador
    var result = listaDeDias.foldLeft(xs)((a,b) => if(a.contains(b)){a.+(b -> (a.get(b).get + 1))} else {a.+(b->1)})
    return result.toList
    
  }
    

  def clienteQueMasMinutosHablo(mes: Int):Cliente = {
    
    return clientes.keys.foldRight(new Cliente(""))((a, b) => if(cantMinutosDeUnMes(mes, a) <= cantMinutosDeUnMes(mes, b)) {b} else a)
    
  }
  
  def montoAFacturarEnUnMes(mes: Int, cliente: Cliente): Double = {
	//Obtengo del monto sin aplicarle los descuentos por plan 
    var montoSinDescuento = cliente.comunicaciones.filter(a => a.fechaComunicacion().getMonth().equals(mes)).foldLeft(0.0)((a, b) => a + valorComunicacion(b))
    var descuento = cliente.comunicaciones.filter(a => a.fechaComunicacion().getMonth().equals(mes)).foldLeft(0.0)((a, b) => a + descuentoPorPlan(b, cliente, Empresa.valorComunicacion(b)))
    //Sumo las cantidades por plan contratado
    var precioPorPlanes = clientes.get(cliente).get.foldLeft(0)((a,b) => 
a + b.precioPlan)
    return montoSinDescuento - descuento + precioPorPlanes
  }
  
  def descuentoPorPlan(comunicacion: Comunicacion, cliente: Cliente, sinDescuento: Double): Double = {
    //Obtengo el precio de la comunicacion aplicando descuentos por cada plan
    var sinDescuentoTotal = sinDescuento * clientes.get(cliente).get.size 
    var conDescuento = clientes.get(cliente).get.foldLeft(0.0)((a,b) => a + b.precioComunicacion(comunicacion))
    return sinDescuentoTotal - conDescuento
    
  }
  
  def agregarPlan(cliente: Cliente, plan: Plan) {
    
    clientes.+=(cliente -> clientes.get(cliente).get.::(plan))
    
  }
  
    
  def combinacionDePlanConveniente(cliente: Cliente, combinacionesDePlanes: List[List[Plan]]): List[Plan] = {
    //Dadas las combinaciones de planes, me quedo con la que me requiera menos dinero
    return combinacionesDePlanes.foldLeft(combinacionesDePlanes.first)((a,b) => if(conviene(cliente.comunicaciones, a, b)){a} else{b})
    
  }
  
  
  def conviene(comunicaciones: List[Comunicacion], planesA: List[Plan], planesB: List[Plan]): Boolean = {
    //Comparo el precio de mis comunicaciones con ambas combinaciones de planes
    val precioPlanesA = planesA.foldLeft(0.0)((a,b) => a + b.precioComunicaciones(comunicaciones))
    val precioPlanesB = planesB.foldLeft(0.0)((a,b) => a + b.precioComunicaciones(comunicaciones))
    
    return precioPlanesA < precioPlanesB
    
  }

  def clienteConFacturaMasAbultada(mes: Int): Cliente = {
    
    return clientes.keys.foldLeft(new Cliente(""))((a,b) => if (montoAFacturarEnUnMes(mes, a) <= montoAFacturarEnUnMes(mes, b)) {b} else a)
   
  }

  def clienteConMasComunicacionesLargas(mes: Int): Cliente = {
    
    return clientes.keys.foldLeft(new Cliente(""))((a,b) => if (comunicacionesLargas(mes, a).length <= comunicacionesLargas(mes, b).length) {b} else a )
    
  }
  
}